package com.bckj.fastboot.upms.biz.core.perm;

import MathBasic.Managements.Data.BinarySet;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.bckj.fastboot.core.redis.RedisOps;
import com.bckj.fastboot.upms.api.dto.PermsCreateParams;
import com.bckj.fastboot.upms.api.dto.TreeSelect;
import com.bckj.fastboot.upms.api.entity.SysMenu;
import com.bckj.fastboot.upms.api.entity.SysRoleInfo;
import com.bckj.fastboot.upms.api.entity.SysUserInfo;
import com.bckj.fastboot.upms.biz.core.perm.model.GenTreeContext;
import com.bckj.fastboot.upms.biz.repository.SysRoleInfoRepository;
import com.bckj.fastboot.upms.biz.repository.SysUserInfoRepository;
import com.bckj.fastboot.upms.biz.repository.SysUserRoleRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Component
@RequiredArgsConstructor
public class PermsHandler {

    private final RedisOps redisOps;
    private final SysRoleInfoRepository sysRoleInfoRepository;
    private final SysUserInfoRepository sysUserInfoRepository;
    private final SysUserRoleRepository sysUserRoleRepository;

    public List<SysRoleInfo> getRoleInfoList(PermsCreateParams params) {
        return sysRoleInfoRepository.getRoleInfoList(params);
    }

    public List<SysUserInfo> getUserInfoList(PermsCreateParams params) {
        return sysUserInfoRepository.getUserInfoList(params);
    }

    public List<Long> getUserRoleIds(Long userId) {
        return sysUserRoleRepository.getUserRoleIds(userId);
    }

    public Perms create(PermsCreateParams params) {
        Perms perms = new Perms(params);
        load(perms, params);
        return perms;
    }

    private void load(Perms perms, PermsCreateParams params) {
        switch (params.getType()) {
            case 0://角色
                if (params.isReadOneself()) {
                    for (SysRoleInfo roleInfo : getRoleInfoList(params)) {
                        perms.setPermissions(roleInfo.getKeyIndex(), roleInfo.getData(), roleInfo.getFilter());
                    }
                }
                break;
            case 1://用户
                if (params.isReadOneself()) {
                    for (SysUserInfo userInfo : getUserInfoList(params)) {
                        perms.setPermissions(userInfo.getKeyIndex(), userInfo.getData(), userInfo.getFilter());
                    }
                }
                if (params.isReadBindings()) {
                    for (Long roleId : getUserRoleIds(params.getId())) {
                        load(perms, new PermsCreateParams()
                                .setId(roleId)
                                .setType(0)
                                .setPermsType(perms.getPermsType())
                                .setReadOneself(true)
                                .setReadBindings(true)
                        );
                    }
                }
                break;
        }
    }

    public TreeSelect genTreeSelect(GenTreeContext ctx) {
        var menuList = ctx.getMenuList();
        Assert.notEmpty(menuList, "菜单列表不能为空");
        Perms perms = ctx.getPerms();
        Perms userPerms = ctx.getUserPerms();
        int dataFilterFlag = ctx.getDataFilterFlag();
        int filter = ctx.getFilter();
        TreeSelect root = new TreeSelect().setLabel("ROOT");
        if (filter == 1) {
            menuList = filterMenuList(menuList, perms);
            Map<Long, List<TreeSelect>> groupMap = menuList.stream().map(TreeSelect::new).collect(Collectors.groupingBy(TreeSelect::getParentId));
            List<TreeSelect> rootList = groupMap.get(0L);
            if (CollUtil.isNotEmpty(rootList)) {
                doChildren(rootList, groupMap);
            }
            root.setChildren(rootList);
            return root;
        } else if (filter == 2) {
            menuList = filterMenuList(menuList, userPerms);
        }
        List<TreeSelect> list = buildTreeSelect(perms, menuList,  dataFilterFlag);
        root.setChildren(list);
        return root;
    }

    private List<SysMenu> filterMenuList(List<SysMenu> menuList, Perms perms) {
        if (perms != null) {
            return menuList.stream()
                    .filter(p -> perms.verify(p.getKeyIndex(), p.getBitLocation()))
                    .collect(Collectors.toList());
        }
        return menuList;
    }

    private List<TreeSelect> buildTreeSelect(Perms perms, List<SysMenu> menuList, int dataFilterFlag) {
        Map<Long, List<TreeSelect>> groupMap = menuList.stream()
                .map(p -> {
                    TreeSelect t = new TreeSelect(p);
                    if (perms != null && perms.verifyPermissionByKeyIndex(p.getKeyIndex())) {
                        BinarySet data = Perms.getDefBinarySet();
                        //0 data + filter 1 data 2 filter
                        if (dataFilterFlag == 1) {
                            data.Inject(perms.getPermissionByKeyIndex(p.getKeyIndex()).get_Data(), false);
                        } else if (dataFilterFlag == 2) {
                            data.Inject(perms.getPermissionByKeyIndex(p.getKeyIndex()).get_Filter(), false);
                        } else {
                            data.Inject(perms.getPermissionByKeyIndex(p.getKeyIndex()).get_Data(), false);
                            data.InjectFilter(perms.getPermissionByKeyIndex(p.getKeyIndex()).get_Filter(), false);
                        }
                        if (data.VerifyByOrdinal(p.getBitLocation())) {
                            t.setSelected(true);
                        }
                    }
                    return t;
                })
                .collect(Collectors.groupingBy(TreeSelect::getParentId));
        List<TreeSelect> rootList = groupMap.get(0L);
        if (CollUtil.isNotEmpty(rootList)) {
            doChildren(rootList, groupMap);
        }
        return rootList;
    }

    private void doChildren(List<TreeSelect> list, Map<Long, List<TreeSelect>> groupMap) {
        list.forEach(p -> {
            List<TreeSelect> children = groupMap.get(p.getId());
            if (CollUtil.isNotEmpty(children)) {
                p.setChildren(children);
                doChildren(children, groupMap);
            }
        });
    }

    public void traverseTreeSelect(Perms perms, TreeSelect ts) {
        if (ts != null && ts.getChildren() != null) {
            for (TreeSelect item : ts.getChildren()) {
                setPermsByTreeSelect(perms, item, true, item.getSelected());
                if (item.getChildren() != null && item.getChildren().size() > 0) {
                    traverseTreeSelect(perms, item);
                }
            }
        }
    }

    private void setPermsByTreeSelect(Perms perms, TreeSelect ts, Boolean isData, Boolean isSelected) {
        if (ts.getValue() == null || perms == null) {
            return;
        }
        String[] nodeValue =  ts.getValue().split(",");
        if (isSelected) {
            if (isData) {
                perms.injectByKeyIndex(Integer.parseInt(nodeValue[0]), Integer.parseInt(nodeValue[1]));
            }
        } else {
            if (isData) {
                perms.removeByKeyIndex(Integer.parseInt(nodeValue[0]), Integer.parseInt(nodeValue[1]));
            }
        }
    }

    public void trans(Perms perms, String values, boolean isData) {
        Stream.of(values.split(",")).forEach(p -> {
            String[] vs = p.split("-");
            int i = Integer.parseInt(vs[0]);
            int b = Integer.parseInt(vs[1]);
            if (isData) {
                perms.injectByKeyIndex(i, b);
            } else {
                perms.injectFilterByKeyIndex(i, b);
            }
        });
    }

    public Set<String> getMyPerms(List<SysMenu> menuList, Perms perms) {
        return menuList.stream().filter(t -> {
            BinarySet data = Perms.getDefBinarySet();
            data.Inject(perms.getPermissionByKeyIndex(t.getKeyIndex()).get_Data(), false);
            data.InjectFilter(perms.getPermissionByKeyIndex(t.getKeyIndex()).get_Filter(), false);
            return data.VerifyByOrdinal(t.getBitLocation());
        }).map(SysMenu::getPerms).collect(Collectors.toSet());
    }

    public Map<String, Set<String>> getMyPermsMap(List<SysMenu> menuList, Perms perms) {
        Map<Long, List<SysMenu>> groupMap = menuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId));
        List<SysMenu> rootList = groupMap.get(0L);
        if (CollUtil.isEmpty(rootList)) {
            return null;
        }
        Map<String, Set<String>> result = new HashMap<>();
        rootList.forEach(p -> {
            List<SysMenu> list = groupMap.get(p.getId());
            if (CollUtil.isNotEmpty(list)) {
                Set<String> permsList = getMyPerms(list, perms);
                result.computeIfAbsent(p.getPerms(), k -> new HashSet<>()).addAll(permsList);
                list.forEach(p1 -> {
                    List<SysMenu> list1 = groupMap.get(p1.getId());
                    if (CollUtil.isNotEmpty(list1)) {
                        Set<String> permsList1 = getMyPerms(list1, perms);
                        result.computeIfAbsent(p.getPerms(), k -> new HashSet<>()).addAll(permsList1);
                    }
                });
            }
        });
        return result;
    }

    public int[] genKeyIndex(String clientId, int permsType) {
        String key = "commons:gen_menu:" + clientId + "_" + permsType;
        String bef = (String) redisOps.get(key);
        if (bef == null) {
            bef = "0000";
        }
        int index = Integer.parseInt(bef.substring(0, 2));
        int bit = Integer.parseInt(bef.substring(2, 4));
        if (bit == 63) {
            bit = 1;
            index = index + 1;
        } else {
            bit = bit + 1;
        }
        String r = String.format("%02d%02d", index, bit);
        redisOps.set(key, r);
        return new int[] {index, bit};
    }

    public void savePerms(Perms perms) {
        if (perms.getType() == 0) {
            sysRoleInfoRepository.lambdaUpdate().eq(SysRoleInfo::getRoleId, perms.getId()).eq(SysRoleInfo::getPermsType, perms.getPermsType()).remove();
            perms.getPermissionsMap().forEach((k ,v) -> {
                SysRoleInfo roleInfo = new SysRoleInfo();
                roleInfo.setRoleId(perms.getId());
                roleInfo.setData(perms.getPermissionByKeyIndex(k).get_Data());
                roleInfo.setFilter(perms.getPermissionByKeyIndex(k).get_Filter());
                roleInfo.setPermsType(perms.getPermsType());
                roleInfo.setKeyIndex(k);
                sysRoleInfoRepository.save(roleInfo);
            });
        } else {
            sysUserInfoRepository.lambdaUpdate().eq(SysUserInfo::getUserId, perms.getId()).eq(SysUserInfo::getPermsType, perms.getPermsType()).remove();
            perms.getPermissionsMap().forEach((k, v) -> {
                SysUserInfo userInfo = new SysUserInfo();
                userInfo.setUserId(perms.getId());
                userInfo.setData(perms.getPermissionByKeyIndex(k).get_Data());
                userInfo.setFilter(perms.getPermissionByKeyIndex(k).get_Filter());
                userInfo.setPermsType(perms.getPermsType());
                userInfo.setKeyIndex(k);
                sysUserInfoRepository.save(userInfo);
            });
        }
    }
}
